#ifndef BOUNDEDCONTAINERSTRATEGY_H
#define BOUNDEDCONTAINERSTRATEGY_H

#pragma once

#include "../../ToolkitLayer.h"
#include <LDF/VisComponentStrategy.h>
#include <IBuffer/IBuffer.h>
#include "../../Utils/Spline.h"

class Point3f;

/**
 * This strategy provides a container that is bounded by a spline curve. The
 * bounds can be edited by manipulating the spline control points.
 *
 * This is based on the BoundedContainerGL class in the original implementation
 * of the Tabletop Framework done by T. Isenberg and A. Miede in the
 * Interactions Lab at the University of Calgary, Canada.
 *
 * Recent changes include changes to the constructor and some of the functions
 *
 * WARNING: This container does not deal well with constantly updating new splines.
 *
 * @author Fabricio Anastacio - fabriciocfa@yahoo.com
 * @author Petra Isenberg - petra.isenberg@ucalgary.ca
 * @since August 6, 2008 (adaptation)
 */
class TOOLKITLAYER_API BoundedContainerStrategy : public VisComponentStrategy
{
	// Attributes
protected:
	/** The spline defining the outer bounds of the container. */
	Spline outerSpline;
	/** The spline defining the border of the outer bounds of the container. */
	Spline outerBorderSpline;
	/** The color of the associated component. */
	float color[4];
	/** The color of the border of the associated component. */
	float borderColor[4];
	/** The color of the border or a control point when selected. */
	float selectedColor[4];

	/** The width of the container border. */
	float borderWidth;

	/** The number of control points in the splines. */
	unsigned int numControlPoints;
	/** The level of the splines. */
	unsigned int splineLevels;

	/** The display list identifier for the inner region. */
	unsigned int dlInnerRegion;
	/** The display list identifier for the border. */
	unsigned int dlOuterBorder;
	/** The display list identifier for the border when selected. */
	unsigned int dlOuterBorderSelected;
	/** The display list identifier for the border when picking. */
	unsigned int dlOuterBorderPicking;

	/** The index of the currently selected control point (-1, if none). */
	int selectedControlPoint;
	/** Indicates that the border is currently selected. */
	bool borderSelected;
	/** The previous x-coordinate of the cursor position. */
	int previousX;
	/** The previous y-coordinate of the cursor position. */
	int previousY;
	/** Whether this component is supposed to be created with a default shape **/
	bool initDefaultShape;

	/** Indicates if the children of the container should be updated after reshaping. */
	bool updateChildrenAfterReshape;

	/** Toggles control point rendering for the boundaries. */
	bool displayControlPoints;
	

	// PRIVATE BUFFERS
	/** This buffer gives a mask to be used for writing the control point values. */
	IBuffer<bool> *maskBuffer;
	/** Mask buffer type identifier. */
	unsigned int maskBufferType;

	/** Button buffer type identifier. */
	unsigned int buttonBufferType;

	// BUTTON BUFFER VALUES
	/** No button value. */
	unsigned char noButtonValue;
	/** Translation area button value. */
	unsigned char translationAreaButtonValue;
	/** RNT area button value. */
	unsigned char rntAreaButtonValue;
	/** List of button values of each control point. */
	std::vector<unsigned char> cpOuterButtonValues;

	
	// Methods
public:

	/**
	* Constructs a bounded container strategy that to begin with has no specific shape.
	*
	*/
	BoundedContainerStrategy();

	/**
	 * Constructs a bounded container strategy with a default shape based on the given number of control points and subdivision level.
	 *
	 * @param nControlPoints the number of control points in the curve.
	 * @param subdivLevels the number of times sundivision should be applied to
	 * the curve.
	 */
	BoundedContainerStrategy(unsigned int nControlPoints, unsigned int subdivLevels);
	/**
	 * Destroys this strategy.
	 */
	~BoundedContainerStrategy(void);

	/**
	 * Initializes this component with a default shape (must be done after
	 * setting this strategy's component).
	 *
	 * @param rounded indicates if the curve shape should be rounded.
	 */
	void initializeDefaultShape(bool rounded);

	/**
	 * Returns a random point inside the inner region of the bounded container.
	 */
	virtual Point3f getRandomInsidePoint();


	// ACCESSOR METHODS
	/**
	 * Returns a copy of the outer spline that defines this container bounds.
	 *
	 * @return the outer spline that defines this container bounds.
	 */
	inline Spline getOuterSpline() { return outerSpline; };
	/**
	 * Sets the outer spline that defines this container bounds.
	 *
	 * @param s the outer spline that defines this container bounds.
	 */
	inline void setOuterSpline(Spline s) { outerSpline = s; numControlPoints = s.getNumControlPoints();};
	/**
	 * Returns the outer border spline that defines this container bounds.
	 *
	 * @return the outer border spline that defines this container bounds.
	 */
	inline Spline getOuterBorderSpline() { return outerBorderSpline; };
	/**
	 * Sets the outer border spline that defines this container bounds.
	 *
	 * @param s the outer border spline that defines this container bounds.
	 */
	inline void setOuterBorderSpline(Spline s) { outerBorderSpline = s; };
	/**
	 * Returns the color used to draw the inner region of the container (using
	 * an array of 4 floats).
	 *
	 * @return the color used to draw the inner region of the container.
	 */
	inline float* getColor() { return color; };
	/**
	 * Sets the color to be used for drawing the inner region of the container
	 * (using an array of 4 floats).
	 *
	 * @param c the color to be used for drawing the inner region of the
	 * container.
	 */
	inline void setColor(float* c) {
		color[0] = c[0]; color[1] = c[1]; color[2] = c[2]; color[3] = c[3];
	};
	/**
	 * Returns the color used to draw the border of the container (using an
	 * array of 4 floats).
	 *
	 * @return the color used to draw the border of the container.
	 */
	inline float* getBorderColor() { return borderColor; };
	/**
	 * Sets the color to be used for drawing the border of the container (using
	 * an array of 4 floats).
	 *
	 * @param c the color to be used for drawing the border of the container.
	 */
	inline void setBorderColor(float* c) {
		borderColor[0] = c[0]; borderColor[1] = c[1]; borderColor[2] = c[2];
		borderColor[3] = c[3];
	};
	/**
	 * Returns the color used to draw the selected border (using an array of 4
	 * floats).
	 *
	 * @return the color used to draw the selected border.
	 */
	inline float* getSelectedColor() { return selectedColor; };
	/**
	 * Sets the color to be used for drawing the selected border (using an
	 * array of 4 floats).
	 *
	 * @param c the color to be used for drawing the selected border.
	 */
	inline void setSelectedColor(float* c) {
		selectedColor[0] = c[0]; selectedColor[1] = c[1]; selectedColor[2] = c[2];
		selectedColor[3] = c[3];
	};
	/**
	 * Returns the value of the border line width.
	 *
	 * @return the value of the border line width.
	 */
	inline float getBorderWidth() { return borderWidth; };
	/**
	 * Sets the value of the border line width.
	 *
	 * @param f the value to be used as the border line width.
	 */
	inline void setBorderWidth(float w) { borderWidth = w; };
	/**
	 * Returns the number of control points in the outer splines.
	 *
	 * @return the number of control points in the outer splines.
	 */
	inline unsigned int getNumControlPoints() { return numControlPoints; };
	/**
	 * Sets the number of control points in the outer splines.
	 *
	 * @param n the number of control points in the outer splines.
	 */
	inline void setNumControlPoints(unsigned int n) { numControlPoints = n; };
	/**
	 * Returns the number of subdivision levels applied to the splines.
	 *
	 * @return the number of subdivision levels applied to the splines.
	 */
	inline unsigned int getSplineLevels() { return splineLevels; };
	/**
	 * Sets the number of subdivision levels applied to the splines.
	 *
	 * @param n the number of subdivision levels applied to the splines.
	 */
	inline void setSplineLevels(unsigned int n) { splineLevels = n; };
	/**
	 * Returns true if the boundary control points should be displayed and
	 * false otherwise.
	 *
	 * @return true if the boundary control points should be displayed.
	 */
	inline bool getDisplayControlPoints() { return displayControlPoints; };
	/**
	 * Sets if the boundary control points should be displayed.
	 *
	 * @param value true if the boundary control points should be displayed.
	 */
	inline void setDisplayControlPoints(bool value) { displayControlPoints = value; };


	/**
	 * Updates the shape of this container by re-evaluating the splines with
	 * the current control points, resizing and repositioning the component,
	 * and resizing and resetting the active buffers.
	 */
	void updateShape(bool recalculateButtonBuffer);
	

protected:

	/**
	 * Calculates the outer border spline from the evaluated outer spline.
	 * This is done from inside evaluateSplines();
	 */
	void calculateOuterBorder();


	/**
	 * Creates the display lists for drawing the container.
	 */
	virtual void createDisplayLists();
	/**
	 * Sets the control points of the bounding spline as an ellipse/rectangular
	 * shape.
	 *
	 * @param rounded if true the container has a rounded shape, otherwise its
	 * shape is rectangular.
	 */
	virtual void setDefaultBounds(bool rounded);

	/**
	 * Adjusts the size of the associated component by obtaining the minimum
	 * and maximum coordinates of the region given by the bounded container
	 * splines (i.e., the axis-aligned bounding rectangle around the component)
	 * and the position of the associated component's center based on the
	 * calculated dimensions.
	 */
	virtual void adjustSizeAndPosition();
	/**
	 * Evaluates the splines for the outer bounds and its border using the
	 * current control points for the outer bounds spline.
	 */
	virtual void evaluateSplines();


	/**
	 * Draws the inner region of the container.
	 */
	virtual void drawInnerRegion();
	/**
	 * Draws the border of the container.
	 *
	 * @param noColor if true the color of the vertices is not set.
	 */
	virtual void drawBorder(bool noColor = false);
	/**
	 * Draws the control points of the bounds spline of the container.
	 */
	virtual void drawControlPoints();

	/**
	 * Moves the control point given the specified index by the given values
	 * along the x- and y-axes.
	 *
	 * @param index the index of the control point.
	 * @param dx the displacement along the x-axis.
	 * @param dy the displacement along the y-axis.
	 */
	virtual void moveControlPointBy(unsigned int index, double dx, double dy);


	/**
	 * Initializes the properties (i. e., i-buffers) of the associated
	 * component.
	 */
	virtual void initProperties();
	/**
	 * Defines the drawing strategy for the associated component.
	 *
	 * @param enablePicking if true, draw for selection mode (adding the
	 * component id).
	 */
	void draw(const std::vector<unsigned long>& selectedIds);
	/**
	 * Defines the drawing strategy for the associated component that should be
	 * used for picking.
	 */
	void drawForPicking();
	/**
	 * Handles the given event.
	 *
	 * @param evt the event to be handled.
	 */
	void onEvent(LargeDisplayEvent* evt);
	/**
	 * Defines the strategy to be used when the associated component is
	 * resized.
	 *
	 * @param width the new width of the component.
	 * @param height the new height of the component.
	 */
	void resize(unsigned int width, unsigned int height);
	

	/**
	 * Fills the button buffer with the inside area, the border (movable), and
	 * the control points (incremental indices after last available).
	 *
	 * @param buffer the buffer to be filled.
	 */
	virtual void fillButtonBuffer(IBuffer<unsigned char>* buffer);
	/**
	 * Initializes the mask buffer with  the shape of a circle to be used for
	 * adding the control points to the button buffer.
	 */
	void fillMaskBuffer();
	/**
	 * Returns the value of the current active button buffer of the associated
	 * component at the given global coordinates. If there is no button buffer
	 * currently active for the associated component or the coordinates are not
	 * inside the buffer, a constant value for NO_BUTTON is returned.
	 *
	 * @param x the x global coordinate to be checked.
	 * @param y the y global coordinate to be checked.
	 * @return the value retrieved from the button buffer or
	 * a constant value for NO_BUTTON in case of failure.
	 */
	unsigned char getButtonValue(unsigned int x, unsigned int y);

	/**
	 * Returns the index of the position that has the given target value in the
	 * given list of button values. If the value is not found, -1 is returned.
	 *
	 * @param values the list of button values.
	 * @param v the value to be looked for.
	 * @return the index of the position that has the given target value in the
	 * given list of button values. If the value is not found, -1 is returned.
	 */
	int obtainIndex(std::vector<unsigned char>& values, unsigned char v);
	/**
	 * Initializes the list with the button values of the control points.
	 */
	virtual void initializeCPButtonValues();

};

#endif // BOUNDEDCONTAINERSTRATEGY_H
